
#include "common/linkage.h"
#include "common/basictypes.h"
#include "i386/mm/page.h"
#include "i386/mm/pgtable.h"

extern int __init_end;

static pmd_t * __init one_md_table_init(pgd_t *pgd) {
  /*
  pud_t *pud;
  pmd_t *pmd_table;

  pmd_table = (pmd_t*)alloc_bootmem_low_pages(PAGE_SIZE);
  set_pgd(pgd, __pgd(__pa(pmd_table) | _PAGE_PRESENT));
  pud = pud_offset(pgd, 0);

  if (pmd_table != pmd_offset(pud, 0)) {
    BUG();
  }

  return pmd_table;
  */
  return 0;
}

static pte_t * __init one_page_table_init(pmd_t* pmd) {
  if (pmd_none(*pmd)) {
    pte_t *page_table = (pte_t*)alloc_bootmem_low_pages(PAGE_SIZE);
    set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE));
    if (page_table != pte_offset_kernel(pmd, 0)) {
      BUG();
    }

    return page_table;
  }

  return pte_offset_kernel(pmd, 0);
}

static inline int is_kernel_text(u32 addr) {
  if (addr >= PAGE_OFFSET && addr <= (u32)__init_end) {
    return 1;
  } else {
    return 0;
  }
}

/*
 * This maps the physical memory to kernel virtual address space, a total
 * of max_low_pfn pages, by creating page tables starting from address
 * PAGE_OFFSET.
 */
static void __init kernel_physical_mapping_init(pgd_t *pgd_base) {
  u32 pfn;
  pgd_t *pgd;
  pmd_t *pmd;
  pte_t *pte;

  int pgd_idx, pmd_idx, pte_ofs;

  pgd_idx = pgd_index(PAGE_OFFSET);
  pgd = pgd_base + pgd_idx;
  pfn = 0;

  for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {
    pmd = one_md_table_init(pgd);

    if (pfn >= max_low_pfn) {
      continue;
    }

    for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD && pfn < max_low_pfn;
         pmd++, pmd_idx++) {
      // 这里还有检查 PSE 的代码， 以下代码是不使用PSE时代代码
      pte = one_page_table_init(pmd);
      for (pte_ofs = 0; pte_ofs < PTRS_PER_PTE && pfn < max_low_pfn;
           pte++, pfn++, pte_ofs++) {
        if (is_kernel_text(address)) {
          set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
        } else {
          set_pte(pte, pfn_pte(pfn, PAGE_KERNEL);
        }
      }
    }
  }
}

static void __init pagetable_init(void) {
  u32 addr;
  pgd_t *pgd_base = swapper_pg_dir;

  // if PSE available
  kernel_physical_mapping_init(pgd_base);  
}

void __init paging_init(void) {
  pagetable_init();
  
  load_cr3(swapper_pg_dir);

  __flush_tlb_all();

  kmap_init();
  zone_sizes_init();
}
